Filter RBM Displacement

A Python Result object can act as a bridge between Mechanical Scripting and ACT Extensions. It allows for both defining user-defined properties as well as creating your own fields –> user-defined plots!

This Python Result user-defined code calculates the (strain-induced) displacement of geometric entities with a mesh node as a moving origin. It also creates a coordinate system that illustrate where the origin is located. Note. The coordinate system is composed of three colored arrows.

To clear the graphics (coordinate system graphics): run Graphics.Scene.Clear() in the Mechanical Scripting shell.

Insert a Python Result under Solution and copy/paste the following code in the Script section:

Transparent

def post_started(sender, analysis):# Do not edit this line
    define_dpf_workflow(this,analysis)

def define_dpf_workflow(this,analysis):
    import mech_dpf
    import Ans.DataProcessing as dpf
    mech_dpf.setExtAPI(ExtAPI)
    scope_val = this.GetCustomPropertyByPath("Geometry/Scoping Property/Geometry Selection").Value
    scope_node = this.GetCustomPropertyByPath("Moving Node Origin/Scoping Property/Geometry Selection").Value.Ids[0]
    scope_ids = scope_val.Ids
    #remote_point_prop = this.GetCustomPropertyByPath("Moving Origin/Remote Point Selection")
    #remote_point = DataModel.GetObjectsByName(remote_point_prop.ValueString)[0]
    #sdata   = Model.Analyses[0].Solution.SolverData
    #rp_data = sdata.GetObjectData(remote_point)
    #rp_id   = rp_data.NodeId
    disp_component = this.GetCustomPropertyByPath("Displacement Component/Component").Value
    component_dict = {1:'X',2:'Y',3:'Z'}
    selected_component = component_dict[int(disp_component)]
    initVecX = []
    initVecY = []
    initVecZ = []
    finalVecX = []
    finalVecY = []
    finalVecZ = []
    
    model = dpf.Model(Model.Analyses[0].ResultFileName)
    dataSource = dpf.DataSources(Model.Analyses[0].ResultFileName)
    my_mesh = model.Mesh
    node = dpf.Scoping()
    node.Location = 'Nodal'
    node.Ids = [scope_node]
    mesh_rp = dpf.operators.mesh.from_scoping(
        scoping = node,
        mesh = my_mesh).outputs.mesh
    nodecoord = dpf.operators.mesh.node_coordinates(
        mesh = mesh_rp).outputs.coordinates.GetDataT1().Data
    displacement_RP = dpf.operators.result.displacement(
        mesh_scoping = node,
        data_sources = dataSource).outputs.fields_container.GetData()[0].Data

    LOCX = nodecoord[0]
    LOCY = nodecoord[1]
    LOCZ = nodecoord[2]
    DEFX = displacement_RP[0] + LOCX
    DEFY = displacement_RP[1] + LOCY
    DEFZ = displacement_RP[2] + LOCZ
    
    nodes = my_mesh.Nodes
    nodeIds = my_mesh.NodeIds
    
    meshdata = ExtAPI.DataModel.MeshDataByName("Global")
    nodes = []
    nodeIds = []
    for current_entity in scope_ids:
        entity_nodes = meshdata.MeshRegionById(current_entity).Nodes
        for n in entity_nodes:
            nodes.append(n)
            nodeIds.append(n.Id)
    selected_scoping = dpf.Scoping()
    selected_scoping.Location = 'Nodal'
    selected_scoping.Ids = nodeIds
    
    for node in nodes:
        initVecX.append(node.X - LOCX)
        initVecY.append(node.Y - LOCY)
        initVecZ.append(node.Z - LOCZ)
    
    displacement_ALL = dpf.operators.result.displacement(
        data_sources = dataSource,
        mesh_scoping = selected_scoping).outputs.fields_container.GetData()

    result_field = dpf.FieldsFactory.CreateScalarField(
        len(nodeIds),
        dpf.locations.nodal)
    result_field.ScopingIds = displacement_ALL[0].ScopingIds
    result_field.Data = initVecZ

    ordered_ids = []
    ordered_values = []
    for node in nodes:
        nId = node.Id
        nX = node.X
        nY = node.Y
        nZ = node.Z
        if selected_component == 'X': 
            UX_ = displacement_ALL[0].GetEntityDataById(nId)[0]
            REL_X = (UX_+nX-DEFX)-(nX-LOCX)
            finalVecX.append((UX_+nX-DEFX)-(nX-LOCX))
            ordered_ids.append(nId)
            ordered_values.append(float(REL_X))
        elif selected_component == 'Y':
            UY_ = displacement_ALL[0].GetEntityDataById(nId)[1]
            REL_Y = (UY_+nY-DEFY)-(nY-LOCY)
            finalVecY.append((UY_+nY-DEFY)-(nY-LOCY))
            ordered_ids.append(nId)
            ordered_values.append(float(REL_Y))
        else: 
            UZ_ = displacement_ALL[0].GetEntityDataById(nId)[2]
            REL_Z = (UZ_+nZ-DEFZ)-(nZ-LOCZ)
            finalVecZ.append((UZ_+nZ-DEFZ)-(nZ-LOCZ))
            ordered_ids.append(nId)
            ordered_values.append(float(REL_Z))
    result_field.ScopingIds = ordered_ids
    result_field.Data = ordered_values
    # Releast .rst file!
    model.ReleaseStreams()
    # Draw graphics
    import units
    geom = Model.Geometry
    analysis = Tree.FirstActiveObject.Parent.Parent
    geoData = analysis.GeoData
    geoUnit = geoData.Assemblies[0].Unit
    lengthConv = units.ConvertUnit(1,geoUnit,'m','Length')
    geom = ExtAPI.DataModel.Project.Model.Geometry
    boundingBoxConv = units.ConvertUnit(1,geom.LengthX.Unit,'m','Length')
    Xscale = geom.LengthX.Value * boundingBoxConv
    Yscale = geom.LengthY.Value * boundingBoxConv
    Zscale = geom.LengthZ.Value * boundingBoxConv
    # Draw graphics
    Graphics.Scene.Clear()
    scaleFactor = .25
    average_scale = (Xscale + Yscale + Zscale)*lengthConv/3
    scaleArrowLength =average_scale
    arrow = ExtAPI.Graphics.Scene.Factory3D.CreateArrow(scaleFactor*average_scale*scaleArrowLength,0.05*average_scale,0.005*average_scale,0.01*average_scale)
    X = DEFX*1e-3
    Y = DEFY*1e-3
    Z = DEFZ*1e-3
    p = ExtAPI.Graphics.CreateWorldPoint(X,Y,Z)
    arrow.Transformation3D.Set(p,ExtAPI.Graphics.CreateVector3D(1,0,0))
    arrow.Color = 0xFF0000
    
    arrow = ExtAPI.Graphics.Scene.Factory3D.CreateArrow(scaleFactor*average_scale*scaleArrowLength,0.05*average_scale,0.005*average_scale,0.01*average_scale)
    arrow.Transformation3D.Set(p,ExtAPI.Graphics.CreateVector3D(0,1,0))
    arrow.Color = 0x00FF00
    
    arrow = ExtAPI.Graphics.Scene.Factory3D.CreateArrow(scaleFactor*average_scale*scaleArrowLength,0.05*average_scale,0.005*average_scale,0.01*average_scale)
    arrow.Transformation3D.Set(p,ExtAPI.Graphics.CreateVector3D(0,0,1))
    arrow.Color = 0x0000FF
    
    dpf_workflow = dpf.Workflow()
    dpf_workflow.SetOutputContour(result_field)
    dpf_workflow.SetOutputWarpField(displacement_ALL)
    dpf_workflow.Record('wf_id', False)
    this.WorkflowId = dpf_workflow.GetRecordedId()

Then, copy/paste the below code under Property Provider:

Transparent

# PROPERTIES #


def reload_props():
    this.PropertyProvider = None
    provider = Provider()
    #group = provider.AddGroup("Moving Origin")
    #this.PropertyProvider = provider
    #options_prop = group.AddProperty("Remote Point Selection", Control.Options)
    #rp_dict = {}
    #bolts = DataModel.GetObjectsByType(DataModelObjectCategory.RemotePoint)
    #for index,bolt in enumerate(bolts): rp_dict.Add(index+1,bolt.Name)
    #options_prop.Options = rp_dict
    
    group_node = provider.AddGroup("Moving Node Origin")
    scoping_prop_node = group_node.AddProperty("Scoping Property", "Scoping", "property_templates")
    
    group_two = provider.AddGroup("Geometry")
    # Add a scoping property to the second group created above
    scoping_prop1 = group_two.AddProperty("Scoping Property", "Scoping", "property_templates")
    
    component = provider.AddGroup("Displacement Component")
    selector = component.AddProperty("Component",Control.Options)
    selector.Options = {1:'X',2:'Y',3:'Z'}
    
    this.PropertyProvider = provider

# region Property Provider Template
from Ansys.ACT.Mechanical.AdditionalProperties import PropertyProviderAdapter
from Ansys.ACT.Mechanical.AdditionalProperties import *
from mech_templates import property_templates
property_templates.set_ext_api(ExtAPI)
class Provider(Ansys.ACT.Interfaces.Mechanical.IPropertyProvider):
    # region These are callbacks that as a user you may want to modify to get specific behavior
    def IsValid(self, prop):
        """
        Called when checking the validity of a property, with the property instance.
        """
        # for double property use the ValidRange property to check validity
        if(isinstance(prop, DoubleProperty)):
            return prop.ValidRange[0] <= prop.Value and prop.ValidRange[1] >= prop.Value
        return True
    def IsReadOnly(self, prop):
        """
        Called when checking if a property should be readonly, with the property instance.
        """
        return False
    def IsVisible(self, prop):
        """
        Called when checking if a property should be visible, with the property instance.
        """
        return True
    def SetValue(self, prop, val):
        """
        Allows you to override the setter of the Value property on the property instance. 
        Keyword Arguments:
            prop -- property of which the value is being set
            val -- the value that was set
        Returns:
            The value that the Value property should be set to
        """
        return val
    def GetValue(self, prop, val):
        """
        Allows you to override the getter of the Value property on the property instance. 
        Keyword Arguments:
            prop -- property of which the value is being set
            val -- current value of the Value property
        Returns:
            The value that the getter on the internal value should return
        """
        return val
    # endregion   
    # structures that hold property instances
    prop_list = []
    prop_map = {}
    prop_groups = set()
    class __AnsGroup():
        """
        Helper group class to group properties, and provides methods to add properties to groups.
        """
        provider = None
        def __init__(self,name=None, provider=None):
            self.name = name
            self.provider = provider
        def __AddScopingProperty(self, name):
            """
            Adds a scoping property with a given name to this group.
            Keyword Arguments : 
                name -- unique name for the scoping property
            """
            scoping_prop = property_templates.ScopingProperty(name, self.name)
            for prop in scoping_prop.GetGroupedProps():
                self.provider.AddProperty(prop)
            return scoping_prop.GetGroupedProps()
        def AddProperty(self, name=None, prop_control=None, module_name=None):
            """
            Creates an instance of the property and connects delgates in 
            the associated Property Propvider.

            Keyword Arguments : 
                name -- unique name for the scoping property
                prop_control -- one of the built in controls, or extended controls
                module_name -- module where the control is defined
            """
            #special case for scoping property
            if(prop_control == "Scoping" and module_name == "property_templates"):
                return self.__AddScopingProperty(name)
            #if no module_name is passed, use the globals in current module
            #that has the built in controls imported
            prop_mod_globals = None
            if(module_name != None):
                if(module_name not in globals()):
                    raise Exception("Unknown module : " + module_name)   
                prop_mod_globals = globals()[module_name].get_globals()
            else:
                prop_mod_globals = globals()
            #class name is built based on control + "Property"
            #    Double - > DoubleProperty
            prop_class_name = str(prop_control) + "Property"
            if(prop_class_name not in prop_mod_globals):
                raise Exception("Unknown property class : " + prop_class_name)
            #instantiate the property based on module and class name
            prop = prop_mod_globals[prop_class_name](self.name + "/" + name, self.name)
            if(prop == None):
                raise Exception("Issue while creating the property instance.")
            #set the delegates to property provider functions
            prop.IsValidCallback = self.provider.IsValid
            prop.IsReadOnlyCallback = self.provider.IsReadOnly
            prop.IsVisibleCallback = self.provider.IsVisible
            prop.GetValueCallback = self.provider.GetValue
            prop.SetValueCallback = self.provider.SetValue
            #as a default make the property name the property display name
            prop.DisplayName = name
            #add property to the provider
            self.provider.AddProperty(prop)
            return prop
    def __init__(self):
        pass
    def GetProperties(self):
        """
        Returns a list of properties in the order that they were added to the property provider. 
        """
        return [self.prop_map[propName] for propName in self.prop_list]
    def AddGroup(self, name=None):
        """
        Creates an instance of helper group class and returns it.
        """
        if name in self.prop_groups:
            raise Exception("Group with name " + name + " already exists, please use a unique group name.")
        #keep groups names so we can make sure no duplicate groups are added
        self.prop_groups.add(name)
        return self.__AnsGroup(name, self)
    def AddProperty(self, prop):
        """
        Method used by the helper group class to add the property to the data-structure holding
        the property instances.
        """
        if(prop.Name in self.prop_map):
            raise Exception("Property name must be unique, property with name '" + prop.Name + "' already exisits.")
        self.prop_list.append(prop.Name)
        self.prop_map[prop.Name] = prop
reload_props()

Right Mouse Click on the Python Result Object and first click Reload Properties, then Connect.

Transparent

Transparent

Fill in the property values according to:

*Moving Node Origin - Select a mesh node that will act as Origin. The result is calculated at the last load step with the deformed position of the node, i.e. not the initial position.

*Geometry - Select the geometric entities where you want to calculate the result.

*Displacement Component - X, Y or Z.

Transparent

Then, Right Mouse Click -> Evaluate All Results.

Note. A native Directional Deformation Z result shows a maximum displacement of 19.75 mm . Here, 10 mm is RBM, hence the strain-induced displacement is 9.75 mm.

Transparent